package cn.xenosp.server.common.aop;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.net.Ipv4Util;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import cn.xenosp.server.common.Constant;
import cn.xenosp.server.common.Result;
import cn.xenosp.server.common.annotation.LogInfo;
import cn.xenosp.server.common.cache.Cache;
import cn.xenosp.server.common.tools.IdUtil;
import cn.xenosp.server.module.login.entity.LoginVo;
import cn.xenosp.server.module.loginLog.entity.SysLoginLog;
import cn.xenosp.server.module.loginLog.service.SysLoginLogService;
import cn.xenosp.server.module.operateLog.entity.SysOperateLog;
import cn.xenosp.server.module.operateLog.service.SysOperateLogService;
import cn.xenosp.server.module.user.entity.SysUser;
import cn.xenosp.server.module.user.service.SysUserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Date;
import java.util.Map;


/**
 * 项目名： service-web
 * 包路径： cn.xenosp.server.common.aop
 * 作者：   TongHui
 * 创建时间: 2025-01-11 15:20
 * 描述: TODO
 * 版本: 1.0
 */
@Aspect
@Component
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
    @Autowired
    Cache redisCache;
    @Autowired
    SysOperateLogService sysOperateLogService;
    @Autowired
    SysLoginLogService sysLoginLogService;
    @Autowired
    SysUserService sysUserService;

    //execution(* cn.xenosp.server.web.controller.*.*(..))
    @Around("@annotation(logInfo)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, LogInfo logInfo) throws Throwable {
        long start = System.currentTimeMillis();
        String id = IdUtil.getId();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String requestPath = attributes != null ? attributes.getRequest().getRequestURI() : "";
        String ip = attributes != null ? ServletUtil.getClientIP(attributes.getRequest()) : "";
        String userAgent = attributes != null ? attributes.getRequest().getHeader("User-Agent") : "";

        Object proceed;
        try {
            if (logInfo.isLogin()) {
                proceed = handleLoginLog(joinPoint, id, ip, userAgent);
            } else {
                proceed = handleOperateLog(joinPoint, id, ip, userAgent, logInfo);
            }
        } catch (Exception e) {
            logger.error("Error during logging: ", e);
            throw e;
        } finally {
            logger.info("{} URI:{} 请求执行时间:{}ms", id, requestPath, (System.currentTimeMillis() - start));
        }
        return proceed;
    }

    /**
     * 处理登录日志的逻辑
     *
     * @param joinPoint  连接点对象
     * @param id         日志ID
     * @param ip         客户端IP地址
     * @param userAgent  客户端User-Agent
     * @return 方法执行结果
     * @throws Throwable 如果方法执行过程中发生异常
     */
    private Object handleLoginLog(ProceedingJoinPoint joinPoint, String id, String ip, String userAgent) throws Throwable {
        SysLoginLog loginLog = new SysLoginLog();
        loginLog.setId(id);
        Object proceed = joinPoint.proceed();
        Result result = (Result) proceed;
        if (result.getCode() == Constant.OK) {
            Map<String, Object> map = (Map<String, Object>) result.getData();
            SysUser user = (SysUser) map.get("user");
            loginLog.setUserId(user.getId());
            loginLog.setUsername(user.getUsername());
            loginLog.setResult(1);
            user.setLoginDate(new Date());
            sysUserService.updateById(user);
        } else {
            LoginVo loginVo = (LoginVo) result.getData();
            loginLog.setUsername(loginVo.getUsername());
            loginLog.setResult(0);
        }
        loginLog.setUserIp(ip);
        loginLog.setUserAgent(userAgent);
        sysLoginLogService.save(loginLog);
        return proceed;
    }

    /**
     * 处理操作日志的逻辑
     *
     * @param joinPoint  连接点对象
     * @param id         日志ID
     * @param ip         客户端IP地址
     * @param userAgent  客户端User-Agent
     * @param logInfo    日志注解信息
     * @return 方法执行结果
     * @throws Throwable 如果方法执行过程中发生异常
     */
    private Object handleOperateLog(ProceedingJoinPoint joinPoint, String id, String ip, String userAgent, LogInfo logInfo) throws Throwable {
        SysOperateLog log = new SysOperateLog();
        log.setId(id);
        String tokenValue = StpUtil.getTokenValue();
        Map<String, Object> cacheMap = (Map<String, Object>) redisCache.get(Constant.TOKEN + "_" + tokenValue);
        if (cacheMap == null) {
            logger.warn("Cache map is null for token: {}", tokenValue);
            return joinPoint.proceed();
        }
        SysUser user = (SysUser) cacheMap.get("user");

        log.setRequestUrl(getRequestPath());
        Object[] args = joinPoint.getArgs();
        Object proceed = joinPoint.proceed();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String methodName = methodSignature.getName();
        String className = methodSignature.getDeclaringType().getName();
        log.setType(logInfo.operationModuleType());
        log.setSubType(logInfo.operationName());
        log.setBizId(logInfo.operationDataModuleId());
        log.setAction(logInfo.operationContent());
        log.setRequestMethod(className + "." + methodName);
        log.setUserId(user.getId());
        log.setUserIp(ip);
        log.setUserAgent(userAgent);
        log.setExtra(JSONUtil.toJsonStr(args));
        sysOperateLogService.save(log);
        return proceed;
    }

    /**
     * 获取请求路径
     *
     * @return 请求路径字符串
     */
    private String getRequestPath() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes != null ? attributes.getRequest().getRequestURI() : "";
    }
}
